defmodule SockWorkstation.SockVOTTSClient.Protocol do
  @moduledoc """
  火山引擎实时语音合成 WebSocket V3 协议实现
  """
  import Bitwise

  alias SockWorkstation.SockVOTTSClient.Protocol
  alias SockWorkstation.SockVOTTSClient.Protocol.{Response, Header, Optional}

  # --- 协议常量 ---
  # @protocol_version 0b0001
  # @default_header_size 0b0001

  # def protocol_version, do: @protocol_version
  # def default_header_size, do: @default_header_size

  # 消息类型
  # @full_client_request 0b0001
  @audio_only_response 0b1011
  @full_server_response 0b1001
  # @error_information 0b1111

  # def full_client_request, do: @full_client_request
  # def audio_only_response, do: @audio_only_response
  # def full_server_response, do: @full_server_response
  # def error_information, do: @error_information

  # 消息类型特定标志位
  @default_msg_type_flag 0b0100
  # @msg_type_flag_no_seq 0b0000
  # @msg_type_flag_positive_seq 0b1
  # @msg_type_flag_last_no_seq 0b10
  # @msg_type_flag_negative_seq 0b11
  # @msg_type_flag_with_event 0b100

  # def default_msg_type_flag, do: @default_msg_type_flag
  # def msg_type_flag_no_seq, do: @msg_type_flag_no_seq
  # def msg_type_flag_positive_seq, do: @msg_type_flag_positive_seq
  # def msg_type_flag_last_no_seq, do: @msg_type_flag_last_no_seq
  # def msg_type_flag_negative_seq, do: @msg_type_flag_negative_seq
  # def msg_type_flag_with_event, do: @msg_type_flag_with_event

  # 序列化方法
  # @no_serialization 0b0000
  # @json 0b0001

  # def no_serialization, do: @no_serialization
  # def json, do: @json

  # 压缩方法
  # @compression_no 0b0000
  # @compression_gzip 0b0001

  # def compression_no, do: @compression_no
  # def compression_gzip, do: @compression_gzip

  # 事件代码
  @event_none 0

  # def event_none, do: @event_none

  # 连接管理事件
  # @event_start_connection 1
  # @event_finish_connection 2
  @event_connection_started 50
  @event_connection_failed 51
  # @event_connection_finished 52

  # def event_start_connection, do: @event_start_connection
  # def event_finish_connection, do: @event_finish_connection
  # def event_connection_started, do: @event_connection_started
  # def event_connection_failed, do: @event_connection_failed
  # def event_connection_finished, do: @event_connection_finished

  # 会话管理事件
  # @event_start_session 100
  # @event_finish_session 102
  @event_session_started 150
  # @event_session_finished 152
  # @event_session_failed 153

  # def event_start_session, do: @event_start_session
  # def event_finish_session, do: @event_finish_session
  # def event_session_started, do: @event_session_started
  # def event_session_finished, do: @event_session_finished
  # def event_session_failed, do: @event_session_failed
  # def event_session_failed(reason), do: {@event_session_failed, reason}

  # 业务事件
  # @event_task_request 200
  # @event_tts_sentence_start 350
  # @event_tts_sentence_end 351
  @event_tts_response 352

  # def event_task_request, do: @event_task_request
  # def event_tts_sentence_start, do: @event_tts_sentence_start
  # def event_tts_sentence_end, do: @event_tts_sentence_end
  # def event_tts_response, do: @event_tts_response

  # --- 数据结构 ---
  defmodule Header do
    # alias SockWorkstation.SockVOTTSClient.Protocol

    @protocol_version 0b0001
    @default_header_size 0b0001
    @full_client_request 0b0001
    @default_msg_type_flag 0b0100
    @no_serialization 0b0000
    @compression_no 0b0000

    defstruct protocol_version: @protocol_version,
              header_size: @default_header_size,
              message_type: @full_client_request,
              message_type_specific_flags: @default_msg_type_flag,
              serial_method: @no_serialization,
              compression_type: @compression_no,
              reserved_data: 0
  end

  defmodule Optional do
    defstruct [
      :event,
      :session_id,
      :sequence,
      :error_code,
      :connection_id,
      :response_meta_json
    ]
  end

  defmodule Response do
    defstruct [
      :header,
      :optional,
      :payload,
      :payload_json
    ]
  end

  def build_header(%Header{} = header) do
    <<
      header.protocol_version <<< 4 ||| header.header_size,
      header.message_type <<< 4 ||| header.message_type_specific_flags,
      header.serial_method <<< 4 ||| header.compression_type,
      header.reserved_data
    >>
  end

  def build_event(event) when event != @event_none do
    <<event::32-signed-big>>
  end

  def build_event(_), do: <<>>

  def build_payload(attrs) do
    payload = %{
      "user" => %{"uid" => attrs[:uid] || "1234"},
      "event" => attrs[:event] || @event_none,
      "namespace" => "BidirectionalTTS",
      "req_params" => %{
        "text" => attrs[:text] || "",
        "speaker" => attrs[:speaker] || "",
        "audio_params" => %{
          "format" => attrs[:audio_format] || "pcm",
          "sample_rate" => attrs[:audio_sample_rate] || 16000
        }
      }
    }

    Jason.encode!(payload)
  end

  @doc """
  解析服务器响应
  """
  def parse_response(data) when is_binary(data) do
    case data do
      <<
        protocol_version::4,
        header_size::4,
        message_type::4,
        message_type_specific_flags::4,
        serial_method::4,
        compression_type::4,
        reserved_data::8,
        rest::binary
      >> ->
        header = %Header{
          protocol_version: protocol_version,
          header_size: header_size,
          message_type: message_type,
          message_type_specific_flags: message_type_specific_flags,
          serial_method: serial_method,
          compression_type: compression_type,
          reserved_data: reserved_data
        }

        {optional, rest} = parse_optional(header, rest)
        {payload, payload_json} = parse_payload(header, optional, rest)

        %Response{
          header: header,
          optional: optional,
          payload: payload,
          payload_json: payload_json
        }

      _ ->
        raise "Invalid response format"
    end
  end

  defp parse_optional(header, data) do
    optional = %Optional{}

    {optional, data} =
      if (header.message_type_specific_flags &&& @default_msg_type_flag) != 0 and
           byte_size(data) >= 4 do
        <<event::32-signed-big, rest::binary>> = data
        {%{optional | event: event}, rest}
      else
        {optional, data}
      end

    {optional, data} =
      case optional.event do
        @event_connection_started ->
          {connection_id, rest} = read_content(data)
          {%{optional | connection_id: connection_id}, rest}

        @event_connection_failed ->
          {meta_json, rest} = read_content(data)
          {%{optional | response_meta_json: meta_json}, rest}

        @event_session_started ->
          {session_id, rest} = read_content(data)
          {meta_json, rest} = read_content(rest)
          {%{optional | session_id: session_id, response_meta_json: meta_json}, rest}

        @event_tts_response ->
          {session_id, rest} = read_content(data)
          {%{optional | session_id: session_id}, rest}

        _ ->
          {optional, data}
      end

    {optional, data}
  end

  defp parse_payload(header, optional, data) do
    case header.message_type do
      @audio_only_response ->
        {payload, _} = read_payload(data)
        {payload, nil}

      @full_server_response ->
        case optional.event do
          @event_tts_response ->
            {payload, _} = read_payload(data)
            {payload, nil}

          _ ->
            if byte_size(data) >= 4 do
              {payload, _} = read_payload(data)
              {nil, payload}
            else
              {nil, nil}
            end

            # _ ->
            #   {nil, nil}
        end
    end
  end

  defp read_content(<<size::32-signed-big, content::binary-size(size), rest::binary>>) do
    {content, rest}
  end

  defp read_payload(<<size::32-signed-big, payload::binary-size(size), rest::binary>>) do
    {payload, rest}
  end
end

defmodule SockWorkstation.SockVOTTSClient do
  @moduledoc """
  火山引擎 TTS WebSocket 客户端
  """

  use WebSockex
  alias SockWorkstation.SockVOTTSClient
  alias SockWorkstation.SockVOTTSClient.Protocol
  alias SockWorkstation.SockVOTTSClient.Protocol.{Response, Header, Optional}

  @session_id_length 12

  @event_start_connection 1
  @event_finish_connection 2
  @event_connection_started 50
  # @event_connection_failed 51
  @event_connection_finished 52

  @event_start_session 100
  @event_finish_session 102
  @event_session_started 150
  # @event_session_finished 152
  # @event_session_failed 153

  @event_task_request 200
  @event_tts_sentence_start 350
  @event_tts_sentence_end 351
  @event_tts_response 352

  defstruct app_id: nil,
            token: nil,
            speaker: nil,
            text: nil,
            output_path: nil,
            session_id: nil,
            sess_id: nil,
            # 明确添加并设置默认值
            connection_id: nil,
            file_ref: nil,
            waiting_for: nil

  def start_link({sess_id, tts_text}) do
    config = %{
      app_id: "5712898771",
      token: "DM1O_wgeItdJocj1Zx7A4nVdM-v_vBOv",
      speaker: "zh_female_wanwanxiaohe_moon_bigtts"
      #  text: "服务器繁忙，请稍后再试。",
      #  output_path: "output.mp3",
    }

    headers = [
      {"X-Api-App-Key", config.app_id},
      {"X-Api-Access-Key", config.token},
      {"X-Api-Resource-Id", "volc.service_type.10029"},
      {"X-Api-Connect-Id", UUID.uuid4()}
    ]

    WebSockex.start_link(
      "wss://openspeech.bytedance.com/api/v3/tts/bidirection",
      __MODULE__,
      %SockVOTTSClient{
        app_id: config.app_id,
        token: config.token,
        speaker: config.speaker,
        text: tts_text,
        output_path: "votts.pcm",
        sess_id: sess_id,
        # session_id: UUID.uuid4() |> String.replace("-", ""),
        session_id: generate_session_id(),
        # session_id: nil,
        file_ref: nil,
        waiting_for: :connection
      },
      extra_headers: headers,
      debug: [:trace],
      async: true
    )
  end

  def handle_connect(_conn, state) do
    Process.send_after(self(), :start_connection, 120)
    {:ok, state}
  end

  def handle_info(:start_connection, state) do
    header = %Header{}

    payload =
      Protocol.build_payload(%{
        event: @event_start_connection
        # speaker: state.speaker
      })

    frame = [
      Protocol.build_header(header),
      Protocol.build_event(@event_start_connection),
      # Protocol.build_optional(optional),
      <<byte_size(payload)::32>>,
      payload
    ]

    {:reply, {:binary, IO.iodata_to_binary(frame)}, %{state | waiting_for: :connection_started}}
  end

  def handle_info(:start_session, state) do
    # IO.puts("Starting session")

    header = %Header{}

    payload =
      Protocol.build_payload(%{
        event: @event_start_session,
        text: state.text,
        speaker: state.speaker
      })

    frame = [
      Protocol.build_header(header),
      <<0, 0, 0, @event_start_session>>,
      <<0, 0, 0, @session_id_length>>,
      state.session_id,
      # Protocol.build_optional(optional),
      <<byte_size(payload)::32>>,
      payload
    ]

    IO.puts("Sending session")
    IO.inspect(frame)
    {:reply, {:binary, IO.iodata_to_binary(frame)}, %{state | waiting_for: :session_started}}
  end

  def handle_info(:send_text, state) do
    IO.puts("Sending text")

    header = %Header{}

    payload =
      Protocol.build_payload(%{
        event: @event_task_request,
        text: state.text,
        speaker: state.speaker
      })

    frame = [
      Protocol.build_header(header),
      Protocol.build_event(@event_task_request),
      <<0, 0, 0, @session_id_length>>,
      state.session_id,
      <<byte_size(payload)::32>>,
      payload
    ]

    # Process.send_after(self(), :finish_text, 1200)
    {:reply, {:binary, IO.iodata_to_binary(frame)}, %{state | waiting_for: :audio_data}}
  end

  def handle_info(:finish_text, state) do
    IO.puts("Finishing text")
    header = %Header{}
    payload = Protocol.build_payload(%{})

    frame = [
      Protocol.build_header(header),
      Protocol.build_event(@event_tts_sentence_end),
      <<0, 0, 0, @session_id_length>>,
      state.session_id,
      <<byte_size(payload)::32>>,
      payload
    ]

    {:reply, {:binary, IO.iodata_to_binary(frame)}, %{state | waiting_for: :audio_data}}
  end


  def handle_info(:finish_session, state) do
    header = %Header{}
    payload = Protocol.build_payload(%{})

    frame = [
      Protocol.build_header(header),
      Protocol.build_event(@event_finish_session),
      <<0, 0, 0, @session_id_length>>,
      state.session_id,
      <<byte_size(payload)::32>>,
      payload
    ]

    {:reply, {:binary, IO.iodata_to_binary(frame)}, %{state | waiting_for: :session_finished}}
  end

  def handle_info(:finish_connection, state) do
    header = %Header{}
    payload = Protocol.build_payload(%{})

    frame = [
      Protocol.build_header(header),
      Protocol.build_event(@event_finish_connection),
      <<byte_size(payload)::32>>,
      payload
    ]

    {:reply, {:binary, IO.iodata_to_binary(frame)}, %{state | waiting_for: :connection_finished}}
  end

  def handle_frame({:binary, data}, state) do
    # IO.inspect(data)
    response = Protocol.parse_response(data)
    IO.inspect(response)

    case {state.waiting_for, response.optional.event} do
      {:connection_started, @event_connection_started} ->
        # IO.puts("Connection started")
        send(self(), :start_session)

        state =
          state
          |> Map.put(:connection_id, response.optional.connection_id)

        {:ok, state}

      {:session_started, @event_session_started} ->
        IO.puts("Session started")
        send(self(), :send_text)
        {:ok, state}

      {:audio_data, @event_tts_sentence_start} ->
        IO.puts("Audio data received")
        {:ok, state}

      {:audio_data, @event_tts_response} ->
        IO.puts("Audio data received #{inspect(byte_size(response.payload))}")
        # SockWorkstation.FrameHelperGen.append_frame(state.sess_id, response.payload)
        File.write!(state.output_path, response.payload, [:binary, :append])
        {:ok, state}

      {:audio_data, @event_tts_sentence_end} ->
        IO.puts("Audio data finished")
        send(self(), :finish_connection)
        {:ok, state}

      {:connection_finished, @event_connection_finished} ->
        {:close, state}

      _ ->
        {:close, state}
    end
  end

  def handle_frame({:text, data}, state) do
    IO.inspect(data)
    {:ok, state}
  end

  def handle_disconnect(_, state) do
    if state.file_ref, do: File.close(state.file_ref)
    {:ok, state}
  end

  defp generate_session_id do
    @session_id_length
    |> :crypto.strong_rand_bytes()
  end
end

# def build_optional(%Optional{} = optional) do
#   []
#   |> add_event(optional.event)
#   |> add_session_id(optional.session_id)
#   |> add_sequence(optional.sequence)
#   |> IO.iodata_to_binary()
#   # |> Enum.reverse()
# end

# def build_session_id_len(_), do: <<>>
# def build_session_id_len(session_id) when byte_size(session_id) == 12 do
#   <<12::32-signed-big>>
#     # session_id
# end

# defp add_session_id(_, session_id) do
#   raise ArgumentError, """
#   Invalid session_id: must be 12-byte raw binary.
#   Got: #{inspect(session_id)} (#{byte_size(session_id)} bytes)
#   """
# end

# defp add_sequence(parts, nil), do: parts
# defp add_sequence(parts, sequence) do
#   [<<sequence::32-signed-big>> | parts]
# end
# def build_optional(%Optional{} = optional) do
#   # parts = []

#   event_parts =
#     if optional.event != @event_none do
#       [<<optional.event::32-signed-big>>]
#     # else
#     #   parts
#     end

#   session_id_parts =
#     if optional.session_id do
#       # session_id_bin =
#         # optional.session_id

#       # |> to_string()
#       # |> String.to_charlist()
#       # |> :binary.list_to_bin()

#       # |> String.to_charlist() |> :binary.list_to_bin()
#       # IO.iodata_to_binary(session_id_bin)
#       # |> to_string()
#       #  |> String.to_charlist()
#       [<<byte_size(optional.session_id)::32-signed-big>>, optional.session_id]
#     # else
#     #   parts
#     end

#   sequence_parts =
#     if optional.sequence do
#       [<<optional.sequence::32-signed-big>>]
#     # else
#     #   parts
#     end

#   IO.iodata_to_binary(event_parts ++ session_id_parts ++ sequence_parts)
# end

# defmodule SockWorkstation.SockVOTTSClient do
#   @moduledoc """
#   火山引擎 TTS 客户端主模块
#   """

#   alias SockWorkstation.SockVOTTSClient.Client

#   # @app_id 5_712_898_771
#   # @secret_key "YOUR_SECRET_KEY"
#   # @access_key "YOUR_ACCESS_KEY"

#   # config = %{
#   #   app_id: "your_app_id",
#   #   token: "your_token",
#   #   speaker: "zh_female_shuangkuaisisi_moon_bigtts",
#   #   text: "火山引擎，让智能增长。欢迎使用火山引擎实时语音合成服务。",
#   #   output_path: "output.mp3"
#   # }

#   # VolcTTSClient.run_demo(config)

#   @doc """
#   运行 TTS 合成示例
#   """
#   def run_demo(config) do
#     {:ok, pid} = Client.start_link(config)
#     # wait_for_completion(pid)
#   end

#   defp wait_for_completion(pid) do
#     receive do
#       {:websocket_terminated, ^pid, _reason} ->
#         :ok
#     after
#       30_000 -> :timeout
#     end
#   end
# end
